home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Mac Game Programming Gurus / TricksOfTheMacGameProgrammingGurus.iso / More Source / C⁄C++ / Tetris Light 1.0 / source / highscore.c < prev    next >
Text File  |  1993-07-18  |  14KB  |  547 lines

  1. /**********************************************************************\
  2.  
  3. File:        highscore.c
  4.  
  5. Purpose:    This module handles the high scores.
  6.             
  7.  
  8. ``Tetris Light'' - a simple implementation of a Tetris game.
  9. Copyright (C) 1993 Hoylen Sue
  10.  
  11. This program is free software; you can redistribute it and/or modify
  12. it under the terms of the GNU General Public License as published by
  13. the Free Software Foundation; either version 2 of the License, or
  14. (at your option) any later version.
  15.  
  16. This program is distributed in the hope that it will be useful,
  17. but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19. GNU General Public License for more details.
  20.  
  21. You should have received a copy of the GNU General Public License
  22. along with this program; see the file COPYING.  If not, write to the
  23. Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  24.  
  25. \**********************************************************************/
  26.  
  27. #include "local.h"
  28.  
  29. #include <Packages.h>
  30.  
  31. #include "dialutil.h"
  32. #include "resources.h"
  33. #include "highscore.h"
  34. #include "windows.h"
  35. #include "pstring.h"
  36.  
  37. /*--------------------------------------------------------------------*/
  38.  
  39. #define NUMBER_HIGH_SCORES    10        /* Maximum number scores recorded */
  40.  
  41. /* Horizontal positions of columns for displaying high scores */
  42.  
  43. #define SCORE_RX            40        /* score right */
  44. #define NAME_X                50        /* name text */
  45. #define DATE_TIME_RX        290        /* date right */
  46. #define DATE_TIME_MAX_WIDTH    80        /* width of date */
  47.  
  48. /*--------------------------------------------------------------------*/
  49.  
  50. /* This structure stores the high score entry whilst it is in memory. */
  51.  
  52. typedef struct {
  53.     LONGINT            date_time;
  54.     unsigned int    score;
  55.     Str255            name;
  56. } High_score;
  57.  
  58. /*--------------------------------------------------------------------*/
  59.  
  60. /* Static globals */
  61.  
  62. static High_score high_score[NUMBER_HIGH_SCORES];
  63.  
  64. static INTEGER base_line_sep; /* Vertical separtion for lines of text */
  65.  
  66. static WindowPtr        high_wind;
  67. static ControlHandle    high_ok_ctrlh;
  68. static Boolean            high_active;
  69.  
  70. /*--------------------------------------------------------------------*/
  71.  
  72. /* Local prototypes */
  73.  
  74. static void highscore_mouseDown(Point);
  75. static void highscore_key(unsigned char code, unsigned char ascii);
  76. static void highscore_update(void);
  77. static void highscore_activate(void);
  78. static void highscore_deactivate(void);
  79.  
  80. static void high_score_button_highlight(Boolean active);
  81.  
  82. /*--------------------------------------------------------------------*/
  83.  
  84. /* Dispatch table for high score window */
  85.  
  86. static Wind_table high_dispatch_table = {
  87.     highscore_mouseDown,
  88.     highscore_key,
  89.     highscore_update,
  90.     highscore_activate,
  91.     highscore_deactivate
  92. };
  93.  
  94. /*--------------------------------------------------------------------*/
  95.  
  96. static void clear_high_scores(void)
  97. /* Reset all high scores to zero, clearing names and dates. */
  98. {
  99.     register int x;
  100.     
  101.     for (x = 0; x < NUMBER_HIGH_SCORES; x++) {
  102.         high_score[x].score = 0;
  103.         high_score[x].date_time = 0;
  104.         *(high_score[x].name) = 0;
  105.     }
  106. }
  107.  
  108. /*--------------------------------------------------------------------*/
  109.  
  110. Boolean highscore_init (void)
  111. /* Initializes the high score module.  Returns FALSE on success, TRUE
  112.    if it failed. */
  113. {
  114.     FontInfo info;
  115.     
  116.     /* Clear scores */
  117.     
  118.     clear_high_scores();
  119.  
  120.     /* Create and setup the display window */
  121.     
  122.     high_wind = GetNewWindow(HIGHSCORE_WINDOW_ID, NIL, NIL);
  123.     if (high_wind == 0)
  124.         return TRUE; /* Failed */
  125.     SetWRefCon(high_wind, (LONGINT) &high_dispatch_table);
  126.     high_active = FALSE;
  127.     
  128.     /* Create the button control that goes inside it */
  129.     
  130.     high_ok_ctrlh = GetNewControl(HIGH_OK_CNTL_ID, high_wind);
  131.     if (high_ok_ctrlh == 0)
  132.         return TRUE; /* Failed */
  133.         
  134.     /* Font baseline separation calculations */
  135.     
  136.     SetPort(high_wind);    
  137.     TextFont(1); /* Application font */
  138.     GetFontInfo(&info);
  139.     base_line_sep = info.ascent + info.descent + info.leading;
  140.     
  141.     return FALSE; /* Success */
  142. }
  143.  
  144. /*--------------------------------------------------------------------*/
  145.  
  146. void highscore_term(void)
  147. /* Finishes up the high score module.  Disposes of the window and 
  148.    control that was created. */
  149. {
  150.     DisposeWindow(high_wind);    /* Controls automatically deleted */    
  151. }
  152.  
  153. /*--------------------------------------------------------------------*/
  154.  
  155. void highscore_start(void)
  156. /* Brings up the high score display window.  Call this routine and
  157.    return back to the main event loop to process the activate and 
  158.    update events that will come. */
  159. {
  160.     SelectWindow(high_wind);
  161.     ShowWindow(high_wind);
  162. }
  163.  
  164. /*--------------------------------------------------------------------*/
  165.  
  166. static void highscore_end(void)
  167. /* Removes the high score display window from view. */
  168. {
  169.     HideWindow(high_wind);
  170. }
  171.  
  172. /*--------------------------------------------------------------------*/
  173.  
  174. static void highscore_mouseDown(Point where)
  175. /* Processes mouseDown events to the high score display window. */
  176. {
  177.     ControlHandle ctrl;
  178.     INTEGER part;
  179.     
  180.     SetPort(high_wind);
  181.     GlobalToLocal(&where);
  182.     
  183.     part = FindControl(where, high_wind, &ctrl);
  184.     if (ctrl != 0)
  185.         if (TrackControl(ctrl, where, NIL))
  186.             highscore_end();
  187. }
  188.  
  189. /*--------------------------------------------------------------------*/
  190.  
  191. static void highscore_key(unsigned char code, unsigned char ascii)
  192. /* Handles key presses when the high score window receives them.  Makes
  193.    the window go away when the return or enter key is pressed. */
  194. {
  195.     if (ascii == RETURN_CODE || ascii == ENTER_CODE) {
  196.         LONGINT final;
  197.         
  198.         HiliteControl(high_ok_ctrlh, 1);
  199.         Delay(8, &final);
  200.         HiliteControl(high_ok_ctrlh, 0);
  201.         
  202.         highscore_end();
  203.     }
  204. }
  205.  
  206. /*--------------------------------------------------------------------*/
  207.  
  208. static void highscore_update(void)
  209. /* Handles update events to the high score display window.  Redraws it. */
  210. {
  211.     register int x;
  212.     register int base_line = base_line_sep;
  213.     Str255 buffer;
  214.     
  215.     SetPort(high_wind);
  216.     EraseRect(&(high_wind->portRect));
  217.     
  218.     DrawControls(high_wind);
  219.     high_score_button_highlight(high_active);
  220.     
  221.     for (x = 0; x < NUMBER_HIGH_SCORES && high_score[x].score > 0; x++) {
  222.         register int len;
  223.     
  224.         /* Display the score */
  225.     
  226.         NumToString(high_score[x].score, buffer);
  227.         MoveTo(SCORE_RX - StringWidth(buffer), base_line);    
  228.         DrawString(buffer);
  229.     
  230.         /* Display the name */
  231.         
  232.         MoveTo(NAME_X, base_line);
  233.         for (len = *(high_score[x].name); len > 0; len--) {
  234.             if (TextWidth(high_score[x].name, 1, len) <
  235.                 DATE_TIME_RX - DATE_TIME_MAX_WIDTH - NAME_X) {
  236.                 DrawText(high_score[x].name, 1, len);
  237.                 
  238.                 if (len != *(high_score[x].name))
  239.                     DrawChar(0xC9); /* ellipsis */
  240.                 break;
  241.             }
  242.         }
  243.         
  244.         /* Display the date */
  245.         
  246.         IUDateString(high_score[x].date_time, shortDate, buffer);
  247.         MoveTo(DATE_TIME_RX - StringWidth(buffer), base_line);
  248.         DrawString(buffer);
  249.  
  250.         base_line += base_line_sep;
  251.     }
  252. }
  253.  
  254. /*--------------------------------------------------------------------*/
  255.  
  256. static void highscore_activate(void)
  257. {
  258.     high_active = TRUE;
  259.     
  260.     SetPort(high_wind);
  261.     high_score_button_highlight(TRUE);
  262.     HiliteControl(high_ok_ctrlh, 0);
  263. }
  264.  
  265. /*--------------------------------------------------------------------*/
  266.  
  267. static void highscore_deactivate(void)
  268. {
  269.     high_active = FALSE;
  270.     
  271.     SetPort(high_wind);
  272.     high_score_button_highlight(FALSE);
  273.     HiliteControl(high_ok_ctrlh, 255);
  274. }
  275.  
  276. /*--------------------------------------------------------------------*/
  277.  
  278. static void high_score_button_highlight(Boolean active)
  279. /* Draws the default button border around the OK button in black or gray
  280.    depending whether `active' is true or not.  Assumed the grafPort has
  281.    been setup ready for drawing. */
  282. {
  283.     Rect rect = (*high_ok_ctrlh)->contrlRect;
  284.     
  285.     if (! active)
  286.         PenPat(gray);
  287.  
  288.     InsetRect(&rect, -4, -4);
  289.     PenSize(3, 3);
  290.     FrameRoundRect(&rect, 16, 16);
  291.     
  292.     PenSize(1, 1);
  293.     PenPat(black);
  294. }
  295.  
  296. /*--------------------------------------------------------------------*/
  297.  
  298. static pascal Boolean hs_filter_proc(DialogPtr dp, EventRecord *evt,
  299.                                      INTEGER *itemhit)
  300. /* Event filter proc for the high score user name entry dialog. */
  301. {
  302.     switch (evt->what) {
  303.     case keyDown:
  304.         if ((evt->message & keyCodeMask) >> 8 == ESC_KEY) {
  305.             simulate_key_hit(dp, Cancel);
  306.             *itemhit = Cancel;
  307.             return TRUE;
  308.         }
  309.         
  310.         switch (evt->message & charCodeMask) {
  311.         case ENTER_CODE:
  312.         case RETURN_CODE:
  313.             simulate_key_hit(dp, OK);
  314.             *itemhit = OK;
  315.             return TRUE;
  316.         case '.':
  317.             if ((evt->modifiers & cmdKey)) {
  318.                 simulate_key_hit(dp, Cancel);
  319.                 *itemhit = Cancel;
  320.                 return TRUE;
  321.             }
  322.             break;
  323.         }
  324.         break;
  325.     case activateEvt:
  326.         if (dp != (WindowPtr) evt->message) {
  327.             if (evt->modifiers & activeFlag)
  328.                 window_activate((WindowPtr) evt->message);
  329.             else
  330.                 window_deactivate((WindowPtr) evt->message);
  331.         }
  332.         break;
  333.     case updateEvt:
  334.         if (dp != (WindowPtr) evt->message) 
  335.             window_update((WindowPtr) evt->message);
  336.         break;
  337.     }
  338.  
  339.     return FALSE;
  340. }
  341.  
  342. void highscore_add(unsigned int score)
  343. /* Tries to enter the `score' in the high score table.  If it will go,
  344.    prompts the user for their name.  If they enter it successfully, the
  345.    table is modified. */
  346. {
  347.     DialogPtr dial;
  348.     INTEGER item;
  349.     INTEGER dummy;
  350.     Handle h;
  351.     Rect box;
  352.  
  353.     if (score <= high_score[NUMBER_HIGH_SCORES - 1].score)
  354.         /* Score too low to get into table - do nothing */
  355.         return;
  356.     
  357.     /* Bring up the name getting dialog */
  358.     
  359.     dial = GetNewDialog(GET_NAME_DIAL_ID, NIL, (WindowPtr) -1);
  360.     GetDItem(dial, NAME_ITEM_NO, &dummy, &h, &box);
  361.     install_hilight_button(dial, OK, NAME_HIGHLIGHT_ITEM_NO);
  362.     SelIText(dial, NAME_ITEM_NO, 0, 32767);
  363.     
  364.     ModalDialog(hs_filter_proc, &item);
  365.     
  366.     if (item == OK) {
  367.         register int index = 0;
  368.         register int i;
  369.         Rect area;
  370.         
  371.         /* Find where new entry goes */
  372.         
  373.         while (score <= high_score[index].score)
  374.             index++;
  375.         
  376.         /* Make room for it */
  377.         
  378.         for (i = NUMBER_HIGH_SCORES - 1; i > index; i--)
  379.             high_score[i] = high_score[i - 1];
  380.         
  381.         /* Put it in */
  382.             
  383.         GetIText(h, high_score[index].name);
  384.         GetDateTime(&(high_score[index].date_time));
  385.         high_score[index].score = score;
  386.  
  387.         /* Force update of display to reflect new scores */
  388.         
  389.         area.left = 0;
  390.         area.right = DATE_TIME_RX;
  391.         area.top = 0;
  392.         area.bottom = area.top + NUMBER_HIGH_SCORES * base_line_sep;
  393.         SetPort(high_wind);
  394.         InvalRect(&area);
  395.         
  396.         /* Display the high score list */
  397.         
  398.         highscore_start();
  399.     }
  400.  
  401.     DisposDialog(dial);
  402. }
  403.  
  404. /*--------------------------------------------------------------------*/
  405.  
  406. Boolean high_score_load(void)
  407. /* Load the high scores from the preference file. Returns FALSE on 
  408.    succes, TRUE if the information was corrupted. */
  409. {
  410.     register int x;
  411.     register unsigned short prev;
  412.     Handle handle;
  413.     Boolean corrupted = FALSE;
  414.     
  415.     /* Read in the score information */
  416.         
  417.     for (x = 0; x < NUMBER_HIGH_SCORES; x++) {
  418.         handle = GetResource(PREF_RSRC_TYPE, HIGH_SCORE_BASE_PREF_ID + x);
  419.         if (handle)
  420.             BlockMove(*handle, high_score + x, SizeResource(handle));
  421.         else
  422.             high_score[x].score = 0;
  423.     }
  424.     
  425.     /* Check correctness */
  426.     
  427.     prev = high_score[0].score;
  428.     for (x = 1; x < NUMBER_HIGH_SCORES; x++) {
  429.         if (high_score[x].score > prev) {
  430.             /* Order has been corrupted, wipe rest */
  431.             corrupted = TRUE;
  432.             high_score[x].score = 0;
  433.             prev = 0;
  434.         } else
  435.             prev = high_score[x].score;
  436.     }
  437.     
  438.     return corrupted;
  439. }
  440.  
  441. /*--------------------------------------------------------------------*/
  442.  
  443. OSErr high_score_save(INTEGER pref_file)
  444. /* Save the high scores to the preference file. */
  445. {
  446.     struct keys **handle;
  447.     OSErr erc;
  448.     register int x;
  449.     
  450.     /* Remove all the entries in the resource file */
  451.     
  452.     for (x = 0; x < NUMBER_HIGH_SCORES; x++) {
  453.         handle = (struct keys **) GetResource(PREF_RSRC_TYPE, HIGH_SCORE_BASE_PREF_ID + x);
  454.         if (handle != NIL && HomeResFile(handle) == pref_file) {
  455.             RmveResource(handle);
  456.             erc = ResError();
  457.             if (erc != noErr)
  458.                 return erc;
  459.             
  460.             DisposHandle(handle);
  461.         }
  462.     }
  463.     
  464.     /* Create the new resource */
  465.     
  466.     for (x = 0; x < NUMBER_HIGH_SCORES && high_score[x].score > 0; x++) {
  467.         LONGINT size = sizeof(LONGINT) + sizeof(unsigned int) + 
  468.                        high_score[x].name[0] + 1;
  469.         
  470.         erc = PtrToHand(high_score + x, &handle, size);
  471.         if (erc != noErr)
  472.             return erc;
  473.         
  474.         AddResource(handle, PREF_RSRC_TYPE, HIGH_SCORE_BASE_PREF_ID + x, "\p");
  475.         erc = ResError();
  476.         if (erc != noErr)
  477.             return erc;
  478.     }
  479.  
  480.     return noErr;
  481. }
  482.  
  483. /*--------------------------------------------------------------------*/
  484.  
  485. /*--------------------------------------------------------------------*/
  486.  
  487. OSErr highscore_save_location(INTEGER pref_file)
  488. /* Saves the saves the position of the high score window. */
  489. {
  490.     OSErr erc;
  491.     LONGINT size;
  492.     Point **handle;
  493.     Rect r;
  494.     
  495.     r = (**(*(WindowPeek)high_wind).contRgn).rgnBBox;
  496.     
  497.     handle = (Point **) GetResource(PREF_RSRC_TYPE, HI_WPOS_PREF_ID);
  498.     if (handle != NIL && HomeResFile(handle) == pref_file) {
  499.         RmveResource(handle);
  500.         erc = ResError();
  501.         if (erc != noErr)
  502.             return erc;
  503.         
  504.         DisposHandle(handle);
  505.     }
  506.     
  507.     /* Create new resource */
  508.     
  509.     erc = PtrToHand(&r, &handle, sizeof(Rect));
  510.     if (erc != noErr)
  511.         return erc;
  512.         
  513.     AddResource(handle, PREF_RSRC_TYPE, HI_WPOS_PREF_ID, "\p");
  514.     erc = ResError();
  515.     if (erc != noErr)
  516.         return erc;
  517.     
  518.     return noErr;
  519. }
  520.  
  521. /*--------------------------------------------------------------------*/
  522.  
  523. void highscore_load_location(void)
  524. /* Tries to read the window position information resource.  This is
  525.    found in the preference file, so it should be open at this stage.
  526.    If it is found and the location places the top right or the top left
  527.    corner in the desktop, it is moved to that location, otherwise it is
  528.    left alone. */
  529. {
  530.     register OSErr erc;
  531.     LONGINT size;
  532.     struct Rect **handle;
  533.     
  534.     handle = (struct Rect **) GetResource(PREF_RSRC_TYPE, HI_WPOS_PREF_ID);
  535.     if (handle) {
  536.         Point ul, ur;
  537.         
  538.         ul.h = (*handle)->left;
  539.         ul.v = (*handle)->top;
  540.         ur.h = (*handle)->right;
  541.         ur.v = (*handle)->top;
  542.         
  543.         if (PtInRgn(ul, GrayRgn) || PtInRgn(ur, GrayRgn))
  544.             MoveWindow(high_wind, (*handle)->left, (*handle)->top, TRUE);
  545.     }
  546. }
  547.